const validateNotBlank = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || ("" + value).trim() === "") {
    callback(new Error(rule.message || "此字段不能为空"));
  } else {
    callback();
  }
}

const validateInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else if (/^(\+|-|)\d+$/.test("" + value)) {
    callback();
  } else {
    callback(new Error(rule.message || "此字段必须是一个整数"));
  }
}

const validateJavaScriptInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else {
    const str = "" + value;
    if ("" + Math.floor(Number(str)) === str) {
      callback();
    } else {
      callback(new Error(rule.message || "此字段必须是一个整数"));
    }
  }
}

const validateNonNegativeInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else if (/^\+?\d+$/.test("" + value)) {
    callback();
  } else {
    callback(new Error(rule.message || "此字段必须是一个非负整数"));
  }
}

const validateNonNegativeJavaScriptInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else {
    const str = "" + value;
    const num = Math.floor(Number(str));
    if (num >= 0 && "" + num === str) {
      callback();
    } else {
      callback(new Error(rule.message || "此字段必须是一个非负整数"));
    }
  }
}

const validatePositiveInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else if (/^\+?0*[1-9]\d*$/.test("" + value)) {
    callback();
  } else {
    callback(new Error(rule.message || "此字段必须是一个正整数"));
  }
}

const validatePositiveJavaScriptInteger = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else {
    const str = "" + value;
    const num = Math.floor(Number(str));
    if (num > 0 && "" + num === str) {
      callback();
    } else {
      callback(new Error(rule.message || "此字段必须是一个正整数"));
    }
  }
}

const commonIdentifierRegExp = /^[_a-z]\w*$/;

const cKeywords = ["if", "else", "do", "while", "for", "goto", "break",
    "continue", "return", "switch", "case", "sizeof", "void", "typedef",
    "struct", "union", "enum", "const", "inline", "auto", "extern", "static",
    "volatile", "register", "default"].reduce(function (accumulator, item) {
      accumulator[item] = true;
      return accumulator;
    }, {});
const cBasicDataTypes = ["signed", "unsigned", "char", "short", "int", "long"]
    .reduce(function (accumulator, item) {
      accumulator[item] = true;
      return accumulator;
    }, {});

const validateCIdentifier = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else {
    const str = ("" + value).toLowerCase();
    if (!commonIdentifierRegExp.test(str) || cKeywords[str] || cBasicDataTypes[str]) {
      callback(new Error(rule.message || "此字段不符合 C / C++ / Java 标识符的要求"));
    } else {
      callback();
    }
  }
}

const sqlKeywords = ["select", "as", "from", "inner", "outer", "left", "right",
    "join", "on", "where", "and", "or", "not", "group", "by", "order",
    "insert", "into", "value", "values", "update", "delete",
    "create", "alter", "drop", "database", "schema", "table", "column",
    "key", "index", "procedure", "primary", "unique", "foreign", "constraint",
    "grant", "if", "else", "then", "case", "when", "end", "with"]
    .reduce(function (accumulator, item) {
      accumulator[item] = true;
      return accumulator;
    }, {});
const sqlDataTypes = ["int", "integer", "smallint", "bigint", "varchar", "char",
    "character", "varying", "float", "double", "decimal", "real", "precision",
    "date", "time", "datetime", "timestamp", "boolean", "byte", "bit", "text",
    "clob", "blob", "binary", "uuid", "json"]
    .reduce(function (accumulator, item) {
      accumulator[item] = true;
      return accumulator;
    }, {});

const validateSQLIdentifier = function (rule, value, callback) {
  if (typeof(value) === "undefined" || value === null || value === "") {
    // allow undefined or null or empty string
    callback();
  } else {
    const str = ("" + value).toLowerCase();
    if (!commonIdentifierRegExp.test(str) || sqlKeywords[str] || sqlDataTypes[str]) {
      callback(new Error(rule.message || "此字段不符合 SQL 标识符的要求"));
    } else {
      callback();
    }
  }
}

export { validateNotBlank, validateInteger, validateJavaScriptInteger,
    validateNonNegativeInteger, validateNonNegativeJavaScriptInteger,
    validatePositiveInteger, validatePositiveJavaScriptInteger,
    validateCIdentifier, validateSQLIdentifier };
